<?php

namespace App\Providers\Foo;

use Illuminate\Support\ServiceProvider;
use App;

/**
 * Class FooServiceProvider
 * 不像传统的加载写法，把很多东西写在一个文件里，
 * service provider可以建立很多个，你还可以为它们起名字，但是加载的时候其实都是一起加载的。
 * 如果写了很多service provider，当然要在配置文件里配置一下，才能全部加载，
 * 于是你就看到 config/app.php 那里面一长串的service provider列表，
 * 同时我们也发现了service provider分开写的好处，就是写第三方包的时候，可以互不干扰
 *
 * @package App\Providers
 */
class FooServiceProvider extends ServiceProvider
{
    /**
     * 若你的提供者仅仅用于绑定注册到服务容器，你可以选择延缓其注册，直到真正需要其中注册的绑定才加载。
     * 设置缓载时, register() 方法依旧会加载, boot() 方法也会执行
     * 所以, 具体缓载的是什么, 还不明白
     * 这里开启或关闭缓载, 在Windows和Linux系统下的执行流程不相同, 待研究
     *
     * @var bool
     */
    protected $defer = false;

    /**
     * Bootstrap the application services.
     * 执行注册后的启动服务
     *
     * @return void
     */
    public function boot()
    {
        // 在应用程序加载时直接执行
        // 这个方法会在所有的服务提供者注册后才被调用，这能让你使用框架中其他所有已注册过的服务
//        de(__FUNCTION__);
    }

    /**
     * Register the application services.
     * 在容器中注册绑定
     *
     * @return void
     */
    public function register()
    {
        // 在应用程序加载时直接执行
//        de(__FILE__);

        // 输出 This is Demo.
        /**
         * IOC容器并不难理解
         * Laravel 有一个核心类，叫做application, 程序启动的时候就实例化了这个类, 放到$app, 这个类就是 ioc容器。
         * 这个容器有啥用呢？
         * 我们知道, Laravel里面存在着大量的Facade用法,
         * 比如你一开始写的router, Router::get(), 你知道为啥可以这样写吗？
         * Router::get() 其实等价于$app->make('router')->get();
         * $app->make('router') 就是一个很形象的ioc容器,
         * $app->make()就是容器, 'router'就是放进去解析的类(的别名), $app->make('router') 就可以把 Route 类的一个实例解析出来。
         *
         * 你可能要问, 为啥要解析呢？解析是什么意思呢？我 New 一下那个类不就可以得到实例了吗？
         * 首先, 如果你把一个类绑定到Ioc的容器后, 它可以自动帮你生成实例, 不用include, 不用new。
         * 其次 new 一个类不能直接帮你解析出它依赖的类, 特别是接口类, 用new的方法你需要先用new创建实例, 然后再传入这个类依赖的实例(意味着有更多的new)
         *
         * 一旦某个类注册到Ioc容器后, 你不但可以很轻松的获得它的实例, 而且可以在这个类里使用依赖注入,
         * 依赖注入包括 构造器注入 和 方法注入,
         * 请注意你之所以能够用type-hint这种方式注入, 而没有真正的传入过任何类的实例,
         * 就是ioc容器在默默为你工作。控制器类的注入就是一个典型的例子。
         *
         * 对于新手还有一点要注意, 可能很长时间你不会真正的去使用ioc容器,
         * 因为laravel已经帮你做了大量的绑定, 你在使用很多注入的时候已经在部分使用Ioc容器的功能了。
         * 另外绑定Ioc容器不是必须的, 例如你注入到Controller里的类,
         * 如果很简单没有更多的依赖关系, 那就不必绑定到ioc容器。
         */
        // 下面三种写法是等价的, 最后一种写法是因为继承了ArrayAccess接口
//        de($this->app->make('App\Providers\Foo\FooBuilder')->rights());
//        de(app()->make('App\Providers\Foo\FooBuilder')->rights());
//        de($this->app['App\Providers\Foo\FooBuilder']->rights());

        // ReflectionException in Container.php line 737: Class FooBuilder does not exist
//        de($this->app['FooBuilder']);
//        $this->app->singleton('FooBuilder', function($app)
//        {
            // 位于同一命名空间, 所以命名空间前缀可省略
//            return new \App\Providers\Foo\FooBuilder($app);
//            return new FooBuilder($app);
//        });
        // 绑定之后, 就可以从容器中成功获取到 FooBuilder 实例
        // 在route和controller中均可以正常使用
//        de($this->app['FooBuilder']);

        // 下面两种绑定方法完全相同, 第一个使用了 Facade 方式
        App::bind('foo', function() {
            return new FooBuilder();
        });
//        $this->app->bind('foo', function() {
//            return new FooBuilder();
//        });
    }

    public function provides()
    {
//        dd(__FUNCTION__);
    }
}
